package lolbatfsm.system;

import java.io.IOException;
import java.io.Serializable;

/**
 * It is the transition between two states of a <code>FSM</code> object.
 * This is an abstract class which implements the behaviour common to all the
 * transitions, but it is inherited by the <code>AsynchronousTransition</code> and
 * <code>SynchronousTransition</code> classes.
 * @author Matteo Battaglio <madbat87@gmail.com>
 */
public abstract class Transition implements Serializable, Comparable<Transition> {

    private final State sourceState;
    private final State destinationState;
    private final Integer duration;
    private transient FSM ownerFSM;
    private transient Integer residualDuration;
    private transient int index;
    private static int instanceCount;

    /**
     * Instantiates a new Transition object
     * @param source the source state
     * @param destination the destination state
     * @param duration the duration of this transition
     */
    public Transition(State source, State destination, Integer duration) {
        try {
            readObject(null);
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        this.sourceState = source;
        this.destinationState = destination;
        this.duration = duration;        
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
        if (in!=null) {
            in.defaultReadObject();
        }
        this.index = instanceCount++;
        this.residualDuration = duration;
    }

    /**
     * Initializes the object.
     * @param ownerFSM the FSM which owns this transition
     */
    public void init(FSM ownerFSM) {
        residualDuration = duration;
        this.ownerFSM = ownerFSM;
    }

    /**
     * This method is called by the FSM after this transition is executed, in order to
     * set the new residual duration.
     * @param elapsedTime
     */
    void decreaseResidualDuration(Integer elapsedTime) {
        if (elapsedTime <= residualDuration) {
            residualDuration -= elapsedTime;
        } else {
            throw new IllegalArgumentException("Elapsed time must be <= residualDuration");
        }
    }

    public Integer getResidualDuration() {
        return this.residualDuration;
    }

    public State getSourceState() {
        return this.sourceState;
    }

    public State getDestinationState() {
        return this.destinationState;
    }

    public FSM getOwnerFSM() {
        return ownerFSM;
    }

    /**
     * Resets the residual duration to the transition's duration.
     */
    void reset() {
        residualDuration = duration;
    }

    @Override
    public String toString() {
        return String.format("[%s] -> [%s], type: %s, residual duration: %d",
                             sourceState,
                             destinationState,
                             this instanceof SynchronousTransition ? "Synchronous" : "Asynchronous",
                             residualDuration);
    }

    public int compareTo(Transition o) {
        if(index > o.index)
            return 1;
        else if(index == o.index)
            return 0;
        else
            return -1;
    }

    public int getIndex() {
        return index;
    }

//    void setOwnerFSM(FSM ownerFSM) {
//        this.ownerFSM = ownerFSM;
//    }


}
